use smallvec::SmallVec;

/*
TODO:
Use Arbitrary to generate structrue data.
https://docs.rs/arbitrary/0.4.4/arbitrary/trait.Arbitrary.html
*/

struct Printer(usize);

impl Drop for Printer {
  fn drop(&mut self) {
    println!("Dropping {}", self.0);
  }
}

struct Bad;

impl Iterator for Bad {
  type Item = Printer;
  
  fn size_hint(&self) -> (usize, Option<usize>) {
   (1, None)
  }
  
  fn next(&mut self) -> Option<Printer> {
    panic!()
  }
}

// https://docs.google.com/document/d/1h3pB0dcPNtAbfB_twbQPh-6FvpIvIZ-xEI3EyGTEHd0/edit
pub fn smallvec1_t_103(data:&[u8]) {
    let vec: SmallVec<[Printer; 0]> = vec![
        Printer(0),
        Printer(1),
        Printer(2),
    ].into();
    
    // vec.insert_many(0, Bad);
}

use arbitrary::{Arbitrary, Unstructured};
// use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured};

#[derive(Arbitrary, Debug)]
enum MyVecOp {
    Push {
        // The size of allocation to make.
        n: u8,
    },
    Grow {
        // Free the index^th allocation we've made.
        n: u8
    },
    Pop {
        n: u8,
    },
}

#[derive(Arbitrary)]
pub struct Rgb {
    pub r: u8,
    pub g: u8,
    pub b: u8,
}

#[derive(Arbitrary)]
struct OpList {
    ops: Vec<MyVecOp>,
}

// let mut v: SmallVec<[u8; 2]> = SmallVec::new();
// v.push(0);
// v.push(1);
// v.push(2); // now capacity is 4
// println!("capacity: {}, inline size: {}", v.capacity(), v.inline_size());
// // grow with the same capacity
// v.grow(4);
// // The backing storage is now freed, here be dragons.
// println!("{:?}", v);
// https://docs.google.com/document/d/1qsNkMLH9nT-qncM16xNj5VMJjHkSyxB8wK9nk4RCiOs/edit
pub fn smallvec2_t_2019_0009(data:&[u8]) {
    // Wrap that raw data in an `Unstructured`.
    let mut unstructured = Unstructured::new(data);
    let mut v: SmallVec<[u8; 2]> = SmallVec::new();
    if let Ok(value) = OpList::arbitrary(&mut unstructured) {
         for op in value.ops {
            let mut o = "";
            let mut on = 0;
            match op {
                MyVecOp::Push { n } => {
                    for i in 0..n {
                      v.push(i.into());
                    }
                    o = "push";
                    on = n;
                }
                MyVecOp::Pop { n } => {
                    for i in 0..n {
                      v.pop();
                    }
                    o = "pop";
                    on = n;
                }
                MyVecOp::Grow { n } => {
                    v.grow(n.into());
                    o = "grow";
                    on = n;
                }
            }
            println!("{}:{} {:?}", o, on, v);
        }
   }
}